package edu.ncsu.csc.ip.routing.dv.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Map;

import edu.ncsu.csc.ip.routing.dv.Node;
import edu.ncsu.csc.ip.routing.dv.RouteEntity;
import edu.ncsu.csc.ip.routing.dv.udp.serialize.NodeSerializer;

/**
 * Class <code>ClientThread</code>
 * 
 * ClientThread send new DV to all neighbors. It waits on it's local Neighbor DV
 * queue if no new DV is available.
 * 
 * @author Moin Ayazifar <mayazif@ncsu.edu>
 * @author Steven Elliott <sdelliot@ncsu.edu>
 * 
 */
public final class ClientThread extends Thread {

	final RouterRemote router;
	protected final DatagramSocket socket;

	public ClientThread(RouterRemote router) throws SocketException {
		super(router.localNode.getName() + "SenderThread");
		this.router = router;
		socket = new DatagramSocket();

	}

	public void run() {
		// send the initial dv
		sendToAllNeighbors();
		try {
			while (true) {
				// Wait until a new DV is produced by neighbors then
				// calculate the new local DV if changed send it to all
				Map<Node, RouteEntity> newDvMap;
				if ((newDvMap = router
						.calculateNewDV(router.updatedNeighborDvQueue.take())) != null) {
					router.localNode.distanceVector = newDvMap;
					sendToAllNeighbors();
				}
			}
		} catch (InterruptedException ex) {
			System.err.println("InterruptedException: " + ex.getMessage());
		}

	}

	private void sendToAllNeighbors() {
		byte[] buf = NodeSerializer.nodeToBytes(router.localNode);
		if (buf.length > RouterRemote.MAX_PACKET_SIZE)
			throw new RuntimeException("Packet size is too big to send");
		// send new DV to all other neighbors
		for (RouteEntity re : router.localNode.neighborsMap.values()) {
			try {
				// send request
				String[] ip_port = re.nextNode.getName().split(
						RouterRemote.IP_PORT_DELIM);
				InetAddress address = InetAddress.getByName(ip_port[0]);
				DatagramPacket packet = new DatagramPacket(buf, buf.length,
						address, Integer.valueOf(ip_port[1]));
				socket.send(packet);
			} catch (Exception e) {
				System.err.println("No DV sent to "
						+ re.nextNode.router.localNode.getName() + ". Reason: "
						+ e.getMessage());
				e.printStackTrace();
			}
		}
	}

}
